Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Comprehensive testing of basic constraint functionality #354

Merged
merged 15 commits into from
May 16, 2018

Conversation

odow
Copy link
Member

@odow odow commented May 11, 2018

Background

At the moment it is pretty hard from the solver's point-of-view to check that they are implementing the full MOI API correctly (e.g., LQOI). This PR attempts to test this.

For every constraint type supported by the solver, it tests the basic usage of the MOI API. It does not test corner cases. That can come later. In most cases, checking that the solver implemented something correctly will involve forming the problem, modifying, solving, and then checking the solution. This PR just tries to ensure that the methods are implemented.

It has exposed a number of bugs an un-implemented stuff in LQOI (ref JuliaOpt/LinQuadOptInterface.jl#10).

See the tests in Gurobi.jl for an example of how it can be called.

What this does

N.B. some unchecked things still todo.

In particular, it adds tests for basic usage of:

  • supportsconstraint
  • canget/get NumberOfConstraints{F,S}()
  • canaddconstraint
  • addconstraint!
  • addconstraints!
  • canget/get ListOfConstraintIndices{F,S}()
  • isvalid
  • candelete/delete!
  • get ConstraintFunction
  • get ConstraintSet
  • canmodify/modify
  • cantransform/transform
  • ConstraintName
  • ConstraintPrimalStart
  • ConstraintDualStart
  • ConstraintBasisStatus
  • ConstraintDual
  • ConstraintPrimal

for the function-set combinations

  • ScalarAffineFunction{Float64}-in-{LessThan, GreaterThan, EqualTo, Interval}
  • ScalarQuadraticFunction{Float64}-in-{LessThan, GreaterThan, EqualTo, Interval}
  • VectorAffineFunction{Float64}-in-{Zeros,Nonnegatives,Nonpositives,Reals}
  • VectorQuadraticFunction{Float64}-in-{Zeros,Nonnegatives,Nonpositives,Reals}
  • SingleVariable-in-{LessThan, GreaterThan, EqualTo, Interval}
  • SingleVariable-in-{ZeroOne, Integer, Semicontinuous, Semiinteger}
  • VectorOfVariables-in-{SOS1, SOS2, Zeros,Nonnegatives,Nonpositives,Reals}
  • conic stuff
    • VectorOfVariables-in-SecondOrderCone
    • VectorOfVariables-in-RotatedSecondOrderCone
    • VectorOfVariables-in-GeometricMeanCone
    • VectorOfVariables-in-ExponentialCone
    • VectorOfVariables-in-DualExponentialCone
    • PowerCone
    • DualPowerCone
    • PositiveSemidefiniteConeTriangle
    • PositiveSemidefiniteConeSquare
    • LogDetConeTriangle
    • LogDetConeSquare
    • RootDetConeTriangle
    • RootDetConeSquare

Considering doing something like

const BasicConstraintTests = Dict(
    (MOI.SingleVariable, MOI.ZeroOne)        => test_singlevariable_in_zeroone
)

function basic_constraint_tests(model::MOI.ModelLike, config::TestConfig; delete::Bool=true)
    for ( (F,S), func ) in BasicConstraintTests
        if MOI.supportsconstraint(model, F, S)
            @testset "$(string(func))" begin
                func(model, config; delete=delete)
            end
        end
    end
end
unittests["basic_constraint_tests"] = basic_constraint_tests

But, we probably need a way for solvers to specify if they don't support deleting or querying a subset of constraint types. I did something like this.

@odow odow changed the title Comprehensive testing of basic constraint functionality WIP: Comprehensive testing of basic constraint functionality May 11, 2018
@codecov-io
Copy link

codecov-io commented May 11, 2018

Codecov Report

Merging #354 into master will decrease coverage by 0.02%.
The diff coverage is 98.38%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #354      +/-   ##
==========================================
- Coverage   96.27%   96.25%   -0.03%     
==========================================
  Files          36       37       +1     
  Lines        4723     4801      +78     
==========================================
+ Hits         4547     4621      +74     
- Misses        176      180       +4
Impacted Files Coverage Δ
src/Test/UnitTests/unit_tests.jl 100% <ø> (ø) ⬆️
src/sets.jl 77.41% <100%> (-0.71%) ⬇️
src/Test/UnitTests/basic_constraint_tests.jl 98.33% <98.33%> (ø)
src/indextypes.jl 62.5% <0%> (ø) ⬆️
src/attributes.jl 94.82% <0%> (+1.21%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e85ea35...ae6abf2. Read the comment docs.

(MOI.VectorOfVariables, MOI.LogDetConeTriangle) => ( dummy_vectorofvariables, 6, MOI.LogDetConeTriangle(3) ),
(MOI.VectorOfVariables, MOI.LogDetConeSquare) => ( dummy_vectorofvariables, 9, MOI.LogDetConeSquare(3) ),
(MOI.VectorOfVariables, MOI.RootDetConeTriangle) => ( dummy_vectorofvariables, 6, MOI.RootDetConeTriangle(3) ),
(MOI.VectorOfVariables, MOI.RootDetConeSquare) => ( dummy_vectorofvariables, 9, MOI.RootDetConeSquare(3) ),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blegat are these right? I'm not sure if I understood the docs correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost, you should do +1 (6->7 and 9->10) for the Det-cones to account for the t

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could check that with the dimension function

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be clarified in the docs? It currently says:
"The argument dimension is the side dimension of the matrix X, i.e., its number of rows or columns."
I took that to mean excluding the t.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the dimension field excludes the t but the dimension function returns the size that a vector set should have to belong to that set so it includes t and all element of the matrices hence it is 1 + d^2 for LogDetSquare where d is the field

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right. I got confused. The docs are clear now that I read them properly.

@odow odow changed the title WIP: Comprehensive testing of basic constraint functionality RFC: Comprehensive testing of basic constraint functionality May 15, 2018
@odow
Copy link
Member Author

odow commented May 15, 2018

This is now RFC. It doesn't attempt to test each method thoroughly. It just tries to check that solvers have implemented the full API for all the F-in-S combinations they claim to support.

This helps avoid situations where you think you've implemented delete! for all constraints, only to find that you missed ScalarAffine-in-Interval. This was helpful for LQOI.

There are a few unchecked items that can probably be addressed in a new PR.

if length(include) > 0 && length(exclude) > 0
error("Don't use `include` and `exclude` together.")
end
test_keys = length(include) > 0 ? include : Iterators.filter(x->!(x in exclude), keys(BasicConstraintTests))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you do test_keys = keys(BasicConstraintTests) in the case where include and exclude are both empty. Since you do if MOI.supportsconstraint later, this would test all supported constraints and remove the need for a ListOfSupportedConstraintTypes discussed in #357 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does test keys(BasicConstraintTests) if include and exclude are both empty.

If we had ListOfSupportedConstraintTypes, we could throw a warning that we didn't test SingleVariable-in-LessThan{Int} for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does test keys(BasicConstraintTests) if include and exclude are both empty.

Indeed ^^

If we had ListOfSupportedConstraintTypes, we could throw a warning that we didn't test SingleVariable-in-LessThan{Int} for example.

The warning would be called when include is non-empty and it does not contain all supported sets ? This can already be done now by iterating through the keys of BasicConstraintTests

@test length(c_indices) == 3 # check that we've added a constraint
@test MOI.candelete(model, c_indices[1])

@test length(c_indices) == 3 # check that we've added a constraint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is already done three lines before


@testset "isvalid" begin
c_indices = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
@test length(c_indices) == 3 # check that we've added a constraint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the comment

# x₁, x₂
const dummy_vectorofvariables = (x::Vector{MOI.VariableIndex}) -> MOI.VectorOfVariables(x)
# 1.0 * x
const dummy_scalar_affine = (x::Vector{MOI.VariableIndex}) -> MOI.ScalarAffineFunction(x, [1.0], 0.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[1.0] should be replaced by ones(Float64, length(x))

@mlubin
Copy link
Member

mlubin commented May 15, 2018

The naming of test_basic_constraint_functionality and basic_constraint_tests is a bit confusing. Are they both public functions that users should care about? They should have parallel names in this case. Is test_basic_constraint_functionality a helper for basic_constraint_tests? do_x is a confusing name for a helper for x_do.

@odow odow merged commit ed7331b into master May 16, 2018
@odow
Copy link
Member Author

odow commented May 16, 2018

Merged this as is. We can address the modification tests after #360 is addressed.

@odow odow deleted the odow/moreunit branch May 16, 2018 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants